import 'dart:convert';
import 'package:dio/dio.dart';

/*
*
* dio网络拦截器-工具类
*
* */



/// api
/// Created by yangjiayi on 2020/10/26.
//超时时⻓ 单位毫秒
const int _CONNECT_TIMEOUT = 10000;
const int _RECEIVE_TIMEOUT = 20000;
///服务端接⼝返回code
//成功
const int CODE_SUCCESS = 200;
//⽆法描述⽆法应对的服务端异常
const int CODE_SERVER_ERROR = 500;
//未登录或token异常
const int CODE_NOT_LOGIN = 302;
//数据异常：直播课程预约失败-时间不对
const int CODE_DATA_EXCEPTION = 321;
//群聊不存在
const int CODE_GROUP_NOT_EXISTS = 315;
//⽆权限：邀请进⼊群聊--你不是群员
const int CODE_NO_AUTH = 305;
//⼊参错误
const int CODE_PARAMETER_ERROR = 300;
// 拉⿊
const int CODE_BLACKED = 353;
// 没有找到回复的主体
const int CODE_NO_DATA = 404;
//验证码错误或。。
const int CODE_ERROR = 304;
//验证码过期或。。、
const int CODE_EXPIRED = 309;
//身份认证的类型
const int AUTH_TYPE_COMMON = 0;
const int AUTH_TYPE_NONE = 1;
const int AUTH_TYPE_TEMP = 2;
//请求⽅法
const String METHOD_GET = "get";
const String METHOD_POST = "post";
Dio _dioGet;
Dio _dioPost;
//通⽤的请求api的⽅法，请在具体的⼦api中进⾏⼊参封装和结果处理 authType只在特定的接⼝中赋值
Future<BaseResponseModel> requestApi(String path, Map<String, dynamic> queryParameters,
    {int authType = AUTH_TYPE_COMMON, String requestMethod = METHOD_POST, bool autoHandleLogout = true,
      CancelToken token}) async {
  BaseResponseModel responseModel;
  try {
    Response response;
    if (requestMethod == METHOD_GET) {
      _setHeaders(authType, _getDioGetInstance());
      response = await _getDioGetInstance().get(path, queryParameters: queryParameters,cancelToken: token);
      print("response：${response.toString()}"); } else {
      _setHeaders(authType, _getDioPostInstance());
      response = await _getDioPostInstance().post(path, queryParameters: queryParameters,cancelToken: token);
    }
    responseModel = BaseResponseModel.fromJson(json.decode(response.toString()));
    //302为未登录 ⼀般统⼀⾃动处理 登出清数据断开⼀些组件连接等操作

          //要注意 只有服务端系统错误500被视为失败 其他错误码要在具体业务中处理
          responseModel.isSuccess = responseModel.code != CODE_SERVER_ERROR;
          return responseModel;
          } on DioError catch (e) {
            responseModel = BaseResponseModel(message: e.message);
            responseModel.isSuccess = false;
            return responseModel;
          } catch (e) {
    responseModel = BaseResponseModel(message: "Unknown error");
    responseModel.isSuccess = false;
    return responseModel;
  }
}
/*
 * 取消请求
 *
 * 同⼀个cancel token 可以⽤于多个请求，当⼀个cancel token取消时，所有使⽤该cancel token的请求都会被取消。
 * 所以参数可选
 */
void cancelRequests({CancelToken token}) {
  if (token == null) {
    var token1 = CancelToken();
    print("取消请求");
    token1.cancel("cancelled");
  } else {
    print("取消请求1");
    token.cancel("cancelled");
  }
}//获取dio单例
Dio _getDioPostInstance() {
  if (_dioPost == null) {
    _dioPost = Dio();
    _dioPost.options.baseUrl = "http://ifdev.aimymusic.com";
    _dioPost.options.connectTimeout = _CONNECT_TIMEOUT;
    _dioPost.options.receiveTimeout = _RECEIVE_TIMEOUT;
    //TODO 还需要更多详细的参数
    //设置拦截器⽤于打印log
    _dioPost.interceptors.add(_LogInterceptors());
    // (_dioPost.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    // (client) {
    // //这⼀段是解决安卓https抓包的问题
    // client.badCertificateCallback =
    // (X509Certificate cert, String host, int port) {
    // return Platform.isAndroid;
    // };
    // client.findProxy = (uri) {
    // return "PROXY 192.168.10.117:8888";
    // };
    // };
  }
  return _dioPost;
}
Dio _getDioGetInstance() {
  if (_dioGet == null) {
    _dioGet = Dio();
    _dioGet.options.connectTimeout = _CONNECT_TIMEOUT;
    _dioGet.options.receiveTimeout = _RECEIVE_TIMEOUT;
    //TODO 还需要更多详细的参数
    //设置拦截器⽤于打印log
    _dioGet.interceptors.add(_LogInterceptors());
    // (_dioGet.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    // (client) {
    // //这⼀段是解决安卓https抓包的问题
    // client.badCertificateCallback =
    // (X509Certificate cert, String host, int port) {
    // return Platform.isAndroid;
    // };
    // client.findProxy = (uri) {
    // return "PROXY 192.168.10.117:8888";
    // };
    // };
  }
  return _dioGet;
}
//因为APP运⾏过程中 headers的参数可能发⽣变化 所以不能在初始化时写死
//TODO 每次请求时都要设置headers是否会降低⽹络请求效率有待测试对⽐
//FIXME 如果token已过期或即将过期则需要做刷新token或重新获取token的操作
void _setHeaders(int authType, Dio dio) {
  //TODO 渠道号暂时写死
  dio.options.headers["aimy-drivers"] ='{\"os\":0,\"clientVersion\":\"1.0.0\",\"channel\":0}';
  //授权认证信息根据个别请求不同取不同的token
  String tokens="bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEwMDQwNzEsInVzZXJfbmFtZSI6IjEwMDQwNzEiLCJzY29wZSI6WyJhcHB1c2VyIiwidWNlbn"
      "RlciIsInVhYSIsInRoaXJkIiwic3BvcnQiXSwiaXNQaG9uZSI6MSwiaXNQZXJmZWN0IjowLCJtaWQiOm51bGwsImFub255bW91cyI6MCwiZXhwIjoxNjI2OTIxODU2LCJncmFudFR"
      "5cGUiOjAsImp0aSI6IjBkYzVkYzZhLTRkMGQtNDg2ZS1hODQ0LTYzMzIyM2IzODE4Mi"
      "IsImNsaWVudF9pZCI6InVzZXJBcHAifQ.KghZTeJ0JamOuraMuE9T8NYNPBCbdwcWW_IapT_bDHw";
  dio.options.headers["Authorization"] = tokens;
  dio.options.headers["user-agent"] = "IFITNESS";
}
//⽤于print log的拦截器
class _LogInterceptors extends InterceptorsWrapper {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler,) {
    print("REQUEST[${options?.headers}][${options
        ?.queryParameters}] ==> URL: ${options?.baseUrl}${options?.path}");
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler,) {
    print(
        "RESPONSE[${response?.statusCode}][${response
            ?.data}] ==> URL: ${response?.requestOptions?.baseUrl}${response
            ?.requestOptions?.path}");
    super.onResponse(response, handler);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler,) {
    print("ERROR[${err?.message}] ==> URL: ${err?.requestOptions?.baseUrl}${err
        ?.requestOptions?.path}");
    super.onError(err, handler);
  }
}

class BaseResponseModel {
  //这个属性是服务端接口返回体中没有的，用于清晰判断请求是否成功
  bool isSuccess;

  Map<String, dynamic> data;
  int code;
  String message;

  BaseResponseModel({this.data, this.code, this.message});

  BaseResponseModel.fromJson(Map<String, dynamic> json) {
    data = json["data"];
    code = json["code"];
    message = json["message"];
  }

  Map<String, dynamic> toJson() {
    var map = <String, dynamic>{};
    map["data"] = data;
    map["code"] = code;
    map["message"] = message;
    return map;
  }
}
